package com.castile.seq;


import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.StringJoiner;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Stream;

/**
 * @description:
 * @author: Castile
 * @create: 2024-04-15 22:58
 * @Version 1.0
 **/
public interface Seq<T> {
    void forEach(Consumer<T> consumer);


    /**
     * 构造只有一个元素的流
     *
     * @param t
     * @param <T>
     * @return
     */
    static <T> Seq<T> unit(T t) {
        return c -> c.accept(t);
    }


    default Seq<T> filter(Predicate<T> predicate) {
        return c -> forEach(t -> {
            if (predicate.test(t)) {
                c.accept(t);
            }
        });
    }

    default <E> Seq<E> map(Function<T, E> function) {

//        return c->forEach(t->c.accept(function.apply(t)));

        return new Seq<E>() {

            /**
             *
             * @param c 此处就是打印的操作
             */
            @Override
            public void forEach(Consumer<E> c) {

                // 最外层传入的操作，比如打印
                // 转换操作： Function
                Seq.this.forEach(t -> c.accept(function.apply(t)));
            }
        };
    }

    default <E> Seq<E> flatMap(Function<T, Seq<E>> function) {
        return c -> forEach(t -> function.apply(t).forEach(c));
//        return new Seq<E>() {
//            @Override
//            public void forEach(Consumer<E> c) {
//                Seq.this.forEach(new Consumer<T>() {
//                    @Override
//                    public void accept(T t) {
//                        function.apply(t).forEach(c);
//                    }
//                });
//            }
//        };
    }

    static <T> T stop() {
        throw StopException.INSTANCE;
    }

    default void consumeTillStop(Consumer<T> consumer) {
        try {
            forEach(consumer);
        } catch (StopException e) {
        }
    }

    default Seq<T> take(int n) {
        return c -> {
            int[] i = {n};
            consumeTillStop(t -> {
                if (i[0]-- > 0) {
                    c.accept(t);
                } else {
                    stop();
                }
            });
        };

    }



    /**
     * onEach
     * 对流的某个元素添加一个操作consumer，但是不执行流——对应Stream.peek。
     *
     * @param consumer
     * @return
     */
    default Seq<T> onEach(Consumer<T> consumer) {
        return c -> forEach(consumer.andThen(c));

//        return new Seq<T>() {
//            @Override
//            public void consume(Consumer<T> c) {
//                consumer.andThen(c);
//            }
//        };


//        return new Seq<T>() {
//            @Override
//            public void consume(Consumer<T> c) {
//                Seq.this.consume(consumer.andThen(c));
//            }
//        };

    }

    /**
     * 流与一个iterable元素两两聚合，然后转换为一个新的流——在Stream里没有对应，但在Python里有同名实现
     *
     * @param iterable
     * @param function
     * @param <E>
     * @param <R>
     * @return
     */
    default <E, R> Seq<R> zip(Iterable<E> iterable, BiFunction<T, E, R> function) {
        return c -> {
            Iterator<E> iterator = iterable.iterator();
            consumeTillStop(t -> {
                if (iterator.hasNext()) {
                    c.accept(function.apply(t, iterator.next()));
                } else {
                    stop();
                }
            });
        };
    }


    default String join(String delimiter) {
        StringJoiner joiner = new StringJoiner(delimiter);
        forEach(t -> joiner.add(t.toString()));
        return joiner.toString();
    }


    default List<T> toList() {
        List<T> list = new ArrayList<>();
        forEach(list::add);
        return list;
    }


    final static class StopException extends RuntimeException {
        public static final StopException INSTANCE = new StopException();

        //        取消这个异常对调用栈的捕获，以减少性能开销（由于是全局单例，不取消也没关系）
        @Override
        public synchronized Throwable fillInStackTrace() {
            return this;
        }
    }
}
